/*
 * RT-Thread Secure
 *
 * Copyright (c) 2021, Shanghai Real-Thread Electronic Technology Co., Ltd.
 *
 * All rights reserved.
 */
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <string.h>

#if defined(RT_USING_SMART)
#include <ioremap.h>
#endif /* RT_USING_SMART */

struct rk3568_mux_pdata
{
    rt_ubase_t reg;
    rt_uint32_t shift;
    rt_uint32_t width;
};
typedef struct rk3568_mux_pdata rk3568_mux_pdata_t;

static rt_err_t set_parent(rt_clk_t *clk, rt_uint8_t index)
{
    rk3568_mux_pdata_t *pdata = (rk3568_mux_pdata_t *)clk->pdata;
    clrsetreg(pdata->reg, (((1 << pdata->width) - 1) << pdata->shift), (index << pdata->shift));
    return RT_EOK;
}

static rt_uint8_t get_parent(rt_clk_t *clk)
{
    rk3568_mux_pdata_t *pdata = (rk3568_mux_pdata_t *)clk->pdata;
    return (readl(pdata->reg) >> pdata->shift) & ((1 << pdata->width) - 1);
}

static struct rt_clk_ops ops =
{
    .set_parent = set_parent,
    .get_parent = get_parent,
};

rt_clk_t *rt_clk_rk3568_mux_register(const char *name, const char *parents, rt_ubase_t reg,
    rt_uint32_t shift, rt_uint32_t width)
{
    rt_clk_t *clk = RT_NULL;
    rk3568_mux_pdata_t *pdata = RT_NULL;
    rt_uint32_t len = 0;
    char *parents_copy = RT_NULL;
    char *parent = RT_NULL;
    rt_uint32_t parent_num = 0;
    rt_uint32_t index = 0;
    char *value = RT_NULL;

    if (!parents)
    {
        goto _fail;
    }

    if((len = rt_strlen(parents)) == 0)
    {
        goto _fail;
    }

    parents_copy = rt_strdup(parents);
    if (parents_copy == RT_NULL)
    {
        goto _fail;
    }

    parent = parents_copy;
    while (strsep(&parent, RT_CLK_MUX_DELIM))
    {
        parent_num++;
    }

    clk = rt_clk_create(name, &ops, parent_num, sizeof(rk3568_mux_pdata_t), 0);
    if(!clk)
    {
        goto _fail;
    }

    /* 处理父时钟字符串 */
    for (parent = parents_copy; (parent < (parents_copy + len)); index++)
    {
        value = parent;
        parent += rt_strlen(parent);
        for (parent += rt_strlen(parent); ((parent < (parents_copy + len)) && (!*parent)); parent++);
        clk->parents[index] = rt_clk_find(value);
    }

    pdata = (rk3568_mux_pdata_t *)clk->pdata;
#if defined(RT_USING_SMART)
    pdata->reg = (rt_ubase_t)rt_ioremap((void *)reg, 4096);
#else
    pdata->reg = reg;
#endif /* RT_USING_SMART */
    pdata->shift = shift;
    pdata->width = width;

    if (rt_clk_register(clk) != RT_EOK)
    {
        goto _fail;
    }

    rt_free(parents_copy);
    parents_copy = RT_NULL;

    return clk;

_fail:
    if(parents_copy)
    {
        rt_free(parents_copy);
        parents_copy = RT_NULL;
    }

    if(clk)
    {
        rt_free(clk);
        clk = RT_NULL;
    }

    return RT_NULL;
}

rt_err_t rt_clk_rk3568_mux_unregister(rt_clk_t *clk)
{
    rt_err_t ret = RT_EOK;

    ret = rt_clk_unregister(clk);
    if(!ret)
    {
        return ret;
    }

    rt_clk_delete(clk);

    return RT_EOK;
}
